import java.util.*;

import org.apache.commons.lang.StringUtils;

/*
(DoublePage: all with author && any is enclosed with quotes) || listOfBookWithTheSameAuthor:
точность = (8) / (18) = 0.(4)
полнота  = (8) / (15) = 0.5(3)


DoublePage: all with author && any is enclosed with quotes:
точность = 5 / 9  = 0.(5)
полнота  = 5 / 15 = 0.(3)
*/

public class DoubleBookClassifier implements Classifier {
    SearchEngine engine;
    BookTrie trie;

    public void setSearchEngine(SearchEngine engine) {
        this.engine = engine;
    }

    public void setTrie(BookTrie trie) {
        this.trie = trie;
    }

    boolean isListOfBookWithTheSameAuthor(List<WebDocument.MentionedBook> relatedBooks, List<String> comments){
        HashMap<String,List<WebDocument.MentionedBook>> authorToBooks = new HashMap<String,List<WebDocument.MentionedBook>>();
        for(WebDocument.MentionedBook book : relatedBooks) {
            HashSet<String> authorParts = new HashSet<String>(Arrays.asList(book.authors));
            for (String authorPart : authorParts) {
                if (!authorToBooks.containsKey(authorPart))
                    authorToBooks.put(authorPart, new ArrayList<WebDocument.MentionedBook>());
                authorToBooks.get(authorPart).add(book);
            }
        }
        for (String author : authorToBooks.keySet()) {
            if (authorToBooks.get(author).size()>=4) {
                boolean isRelated = true;
                for (WebDocument.MentionedBook book : authorToBooks.get(author)) {
                    isRelated &= book.isWithAuthor;
                }
                if (isRelated) {
                    for (WebDocument.MentionedBook book : authorToBooks.get(author)) {
                        comments.add(
                            "\"" + StringUtils.join(book.title," ") + "\" (" + StringUtils.join(book.authors," ") + ")"
                        );
                    }
                    return true;
                }
            }
        }
        return false;
    }

    public ClassifierDecisionInfo makeDecision(String query) throws Exception {
        List<WebDocument> docs = engine.search(query);

        ClassifierDecisionInfo info = new ClassifierDecisionInfo();

        HashMap<Integer,List<WebDocument.MentionedBook>> idToBooks = new HashMap<Integer,List<WebDocument.MentionedBook>>();
        for(WebDocument doc : docs) {
            List<WebDocument.MentionedBook> relatedBooks = doc.getRelatedBooks(trie);
            info.isBookRelatedQuery = isListOfBookWithTheSameAuthor(relatedBooks, info.comments);
            if (info.isBookRelatedQuery) break;
            HashMap<Integer,List<WebDocument.MentionedBook>> docUniqueBooks = new HashMap<Integer,List<WebDocument.MentionedBook>>();
            for(WebDocument.MentionedBook book : relatedBooks) {
                if (book.isWithAuthor) {
                    if (!docUniqueBooks.containsKey(book.bookId)) {
                        docUniqueBooks.put(book.bookId, new ArrayList<WebDocument.MentionedBook>());
                    }
                    docUniqueBooks.get(book.bookId).add(book);
                }
            }
            for (int bookId : docUniqueBooks.keySet()){
                if (idToBooks.containsKey(bookId)){
                    boolean[] flags = new boolean[]{ false, false, false, false };

                    for(WebDocument.MentionedBook book : idToBooks.get(bookId)) {
                        if (book.isWithAuthor) flags[0] = true;
                        if (book.isEnclosedWithQuotes) flags[1] = true;
                    }

                    for(WebDocument.MentionedBook book : docUniqueBooks.get(bookId)) {
                        if (book.isWithAuthor) flags[2] = true;
                        if (book.isEnclosedWithQuotes) flags[3] = true;
                        idToBooks.get(bookId).add(book);
                    }

                    if ((flags[0] || flags[2]) && (flags[1] || flags[3])) {
                        info.isBookRelatedQuery = true;
                        WebDocument.MentionedBook book = idToBooks.get(bookId).get(0);
                        info.comments.add(
                            "\"" + StringUtils.join(book.title," ") + "\" (" + StringUtils.join(book.authors," ") + ")"
                        );
                        break;
                    }
                } else {
                    idToBooks.put(bookId, new ArrayList<WebDocument.MentionedBook>());
                    for(WebDocument.MentionedBook book : docUniqueBooks.get(bookId)) {
                        idToBooks.get(bookId).add(book);
                    }
                }

                if (info.isBookRelatedQuery) break;
            }
            if (info.isBookRelatedQuery) break;
        }

        return info;
    }
}
